# encoding:utf-8
import traceback

import os
import re
import time
from pexpect import run, spawn, EOF, TIMEOUT

from app.modules.apps.container import ContainerApp
from config import BeetleConfig


class SpringBootApp(ContainerApp):
    def __init__(self, app_id,
                 app_name,
                 zone_home_path,
                 zone_alias,
                 port,
                 container_home='',
                 nas_app_package_path=None,
                 app_config_home_path=None,
                 sub_path='',
                 mem_level=1,
                 timezone=''):

        if container_home == '':
            container_home = zone_home_path + '/springboot_' + app_name

        super(SpringBootApp, self).__init__(app_id=app_id,
                                            app_name=app_name,
                                            zone_home_path=zone_home_path,
                                            zone_alias=zone_alias,
                                            port=port,
                                            container_home=container_home,
                                            nas_app_package_path=nas_app_package_path,
                                            app_config_home_path=app_config_home_path,
                                            sub_path=sub_path)

        self._app_home_path = self._container_home

        self._mem_level = mem_level

        self._http_port = str(port)
        self._debug_port = re.sub(r'(\d*)(\d)', r'\g<1>7', self._http_port)
        self._dubbo_port = re.sub(r'(\d*)(\d)', r'\g<1>8', self._http_port)

        # 描述一个tomcat所需的所有端口,这是一个共有属性
        self._ports = [self._http_port,
                       self._debug_port,
                       self._dubbo_port]

        if self._nas_app_package_path == '':
            self._nas_app_package_path = '/mnt/winitRelease/latest.product/%s' % self._app_name

        self._app_config_home_path = self._config_home_path + '/' + self._zone_name + '/' + self._app_name
        self._app_config_home_path += '/src/main/resources'

        self._jar_name = '%s.jar' % self._app_name

        self._log_file_name = "%s/%s.log" % (
            BeetleConfig.APP_LOG_PATH, self._app_id)

        self._log_level = BeetleConfig.UCLOUD_APP_LOG_LEVEL

        self.timezone = timezone

    def set_runtime_param(self, nas_app_package, version='', sub_path=''):
        super(SpringBootApp, self).set_runtime_param(
            nas_app_package=nas_app_package, version=version,
            sub_path=sub_path)

    def install_app(self):

        # 1. 首先根据container_home, 创建文件夹,并清空这个文件夹.
        self._create_app_context()

        # 3. 到package_path目录下找jar包, 验证下, 是不是只有一个, 如果有多个,
        #  抛异常,把唯一的jar包复制到container_home下, 然后改成app_name.jar.
        self._download_package()

        # 5. 处理configz
        self._config_app()

        pass

    def _config_app(self):

        # 1. update config from svn.
        self._update_config()

        if not os.path.isdir(self._app_config_home_path):
            msg = '没有找到配置文件, svn/winit2.0/config/UC 目录下没有该应用的配置文件? %s' \
                  % self._app_config_home_path
            raise Exception(msg)

        # 2. Copy properties files to app config path.
        _log, _result = self.run('/bin/bash -c "cp -rf %s/* %s"' % (
            self._app_config_home_path, self._app_home_path))

        # 3. Change the server port.
        self.replace_config_content(
            file_path=self._app_home_path + '/application.properties',
            pattern=r"([?:^|\n]server.port=)(\d*)",
            repl=r"\g<1>%s" % self._http_port)

        self.replace_config_content(
            file_path=self._app_home_path + '/application.properties',
            pattern=r"(spring.dubbo.port=)(\d*)",
            repl=r"\g<1>%s" % self._dubbo_port)

        self.replace_config_content(
            file_path=self._app_home_path + '/application.properties',
            pattern=r"(logging.file=)(\S*)",
            repl=r"\g<1>%s" % self._log_file_name)

        self.app_log('%s change config file successfully.' % self._app_id)

    def validate(self):
        pass

    def restart(self):
        # 先停后启动
        self.stop()

        _cmd = 'nohup %s/%s.jar </dev/null >/dev/null 2>&1 &' % (
            self._app_home_path, self._app_name)

        child = spawn(_cmd)
        child.sendline('\n')

        self.app_log(
            '%s has been restarted at %s.' % (self._app_id, self._http_port))

        time.sleep(3)

        self.app_log('Checking %s startup logging....' % self._app_id)

        _tail_log_cmd = 'tail -f %s' % self._log_file_name
        child = spawn(_tail_log_cmd)

        p_success = r".*Started \w* in .* seconds .*"
        p_exception = r".*Exception:.*"
        p_exception2 = r".* previous errors .*"

        # expect刷日志会稍微有点延迟, 大概比实际日志的输出慢了半分钟左右.
        index = child.expect(
            pattern=[EOF, TIMEOUT, p_success, p_exception, p_exception2],
            timeout=BeetleConfig.UCLOUD_APP_STARTUP_TIMEOUT)

        if index == 1:
            raise Exception(
                '启动%s 超过%d秒, 请检查应用启动日志和dubbo服务, 确认应用是否正确启动.' % (
                    self._app_id, BeetleConfig.UCLOUD_APP_STARTUP_TIMEOUT))
        elif index > 2:

            time.sleep(5)
            # 暂时还不能停止, 因为很多应用循环依赖,启动一定会抛异常.
            # self.stop()

            raise Exception('在%s的启动日志中发现了Exception关键字, 可能启动失败, 也可能只是警告.'
                            ' 请检查应用启动日志和dubbo控制台, 确认应用是否启动正常.' % self._app_id)

        self.app_log(
            'App %s is startup completely. index=%d' % (self._app_id, index))

        pass

    def _create_app_context(self):

        if self._mem_level == 1:
            _java_opts = r'-Xms384m -Xmx384m ' \
                         r'-XX:PermSize=64m ' \
                         r'-XX:MaxPermSize=256m ' \
                         r'-XX:-UseGCOverheadLimit '
        elif self._mem_level == 2:
            _java_opts = r'-Xms512m ' \
                         r'-Xmx512m ' \
                         r'-XX:PermSize=64m ' \
                         r'-XX:MaxPermSize=256m ' \
                         r'-XX:-UseGCOverheadLimit '
        elif self._mem_level == 3:
            _java_opts = r'-Xms1024m ' \
                         r'-Xmx1024m ' \
                         r'-XX:PermSize=64m ' \
                         r'-XX:MaxPermSize=256m ' \
                         r'-XX:-UseGCOverheadLimit '
        else:
            _java_opts = r'-Xms384m ' \
                         r'-Xmx384m ' \
                         r'-XX:PermSize=64m ' \
                         r'-XX:MaxPermSize=256m ' \
                         r'-XX:-UseGCOverheadLimit '

        self.run('mkdir -p %s' % self._app_home_path)

        _java_debug_opts = r' -Xdebug -Xnoagent -Djava.compiler=NONE ' \
                           r'-Xrunjdwp:transport=dt_socket,address=%s,' \
                           r'server=y,suspend=n' % self._debug_port

        # 3. Generate a memory and log config file for spring-boot app.
        _conf_file = "%s/%s.conf" % (self._app_home_path, self._app_name)

        _conf_file_content = r'JAVA_OPTS="%s %s "' \
                             % (_java_opts, _java_debug_opts)

        with open(_conf_file, 'w') as _f:
            _f.write(_conf_file_content)

    def _download_package(self):

        _target_path = self._nas_app_package_path + self._sub_path

        _result = self.run(
            '/bin/bash -c "ls -l %s|grep .jar|wc -l" '
            % _target_path)

        try:
            _result = int(_result)
        except:
            _result = 0

        if _result != 0:
            raise Exception(
                "在应用包路径%s找不到jar包, 或者jar包多与1个, 请检查安装包路径是否正确   !" % self._nas_app_package_path)

        # copy spring boot的安装文件
        self.run("/bin/bash -c 'cp %s/*.jar %s/%s.jar'" % (
            _target_path, self._app_home_path, self._app_name))

        pass
